/**
 * \file          iniwriting.cpp
 *
 *    Provides some free functions to write INI files, given a
 *    midipp::csvarray object.
 *
 * \library       libmidipp
 * \author        Chris Ahlstrom
 * \date          2014-04-23
 * \updates       2014-05-21
 * \version       $Revision$
 * \license       $XPC_SUITE_GPL_LICENSE$
 *
 *    Provides a way to create INI files from the contents of a
 *    midipp::csvarray object.
 */

#include <fstream>                     /* std::ofstream                       */

#include <csvarray.hpp>                /* midipp::csvarray class              */
#include <ininames.hpp>                /* some stock string values            */
#include <iniwriting.hpp>              /* the functions in this module        */

/**
 *    Provides a less cluttered way to add a newline to string output.
 */

#define _NL     std::endl              /* defined for brevity/formatting      */

namespace midipp
{

/**
 *    Provides explanatory text for the top of the INI file.
 *
 *    This is C++, so we don't have to worry about the length of character
 *    strings.
 */

static const std::string s_explanatory_header =
"# This file provides easy-to-use settings for remapping some MIDI events\n"
"# so that a MIDI file for a specific device plays back in General MIDI (GM).\n"
"# This file consists of one unnamed scction and a number of named sections\n"
"# as described below.  Note that the 'name' values can be empty, if you don't\n"
"# want to key them in.\n"
"#\n"
"# The unnamed section:\n"
"#\n"
"#  file-style.      The format of the INI file.  Values: 'sectioned' (only).\n"
"#  setup-name.      Provides a name for the setup, for information only.\n"
"#  map-type.        Types of maps in this file: 'drum', 'patch', or 'multi'.\n"
"#  gm-channel.      Indicates the drum channel for GM.   Usually 10.\n"
"#  dev-channel.     Drum channel for the device.  Some use channel 16.\n"
"#  extract-channel. Channel to write to MIDI file.  Other channels dropped.\n"
"#  reject-channel.  Channel for events to be dropped from the new MIDI file.\n"
"#  reverse.         Reverse mapping. Usually better as a command-line option.\n"
"#  testing.         Do the latest developer test.  Command-line preferred.\n"
"#\n"
"# The drum section:\n"
"#\n"
"#  [Drum 35].  Marks a drum-change section, one per instrument.\n"
"#\n"
"#  gm-name.         Provides the GM name for the drum assigned to this note.\n"
"#  gm-note.         Provides the GM note number, same as the section number.\n"
"#  dev-name.        Provides device name for the drum assigned to this note.\n"
"#  dev-note.        MIDI note to convert gm-note to for GM-compliant playback.\n"
"#\n"
"# The patch/program section:\n"
"#\n"
"#  [Patch 35].      Marks a patch-change section, one per program.\n"
"#\n"
"#  gm-name.         The name of the patch/program in the GM layout.\n"
"#  gm-patch.        MIDI patch number corresponding to the named instrument.\n"
"#  dev-name.        Name of the device patch corresponding to this MIDI note.\n"
"#  dev-patch.       MIDI patch number to which this patch number is converted.\n"
;

/**
 *    Writes a csvarray object to a file in a simple INI format.
 *
 * @deprecated
 *
 *    The input fields of a Drum map grabbed from a spreadsheet are in the
 *    following order:
 *
 *       -# GM drum name
 *       -# GM drum note number
 *       -# Device drum name
 *       -# Device note number
 *
 * \param filespec
 *    Provides the full path to the file to be processed.
 *
 * \param csv
 *    Provides the csvarray object to be dumped as a simple INI file.
 *
 * \return
 *    Returns 'true' if all operations succeeded.
 */

bool
write_simple_drum_file
(
   const std::string & filespec,
   const csvarray & csv
)
{
   std::ofstream output(filespec.c_str());
   bool result = output.good();
   if (result)
   {
      output
         << "# Generated by 'midicvtpp --csv-simple " << csv.source_file() << _NL
         << "#   --output " << filespec << "'" << _NL
         << "#" << _NL
         ;

      output
         << GM_INI_FILE_STYLE       << " = simple" << _NL
         << GM_INI_SETUP_NAME       << " = "       << csv.name() << _NL
         << GM_INI_MAP_TYPE         << " = drum"   << _NL
         << GM_INI_GM_CHANNEL       << " = 10"     << _NL
         << GM_INI_DEV_CHANNEL      << " = 10"     << _NL   /* some use ch 16 */
         << GM_INI_EXTRACT_CHANNEL  << " = "       << GM_INI_NO_VALUE << _NL
         << GM_INI_REJECT_CHANNEL   << " = "       << GM_INI_NO_VALUE << _NL
         << GM_INI_REVERSE          << " = false"  << _NL
         << GM_INI_TESTING          << " = false"  << _NL
         << _NL
         ;

      csvarray::Rows::const_iterator row;
      for (row = csv.rows().begin(); row != csv.rows().end(); row++)
      {
         csvarray::Fields::const_iterator field = row->begin();
         output
            << DRUM_LABEL_GM_NOTE << "_"  << field[DRUM_INDEX_GM_NOTE] << " = "
            << field[DRUM_INDEX_GM_NAME]  << ","
            << field[DRUM_INDEX_GM_NOTE]  << ","
            << field[DRUM_INDEX_DEV_NAME] << ","
            << field[DRUM_INDEX_DEV_NOTE]
            << _NL
            ;
      }
      output << "# vim: ts=3 sw=3 et ft=dosini" << _NL;

   }
   return result;
}

/**
 *    Writes a csvarray object to a drum file in a sectioned INI format.
 *
 * \param filespec
 *    Provides the full path to the CSV file to be processed.
 *
 * \param csv
 *    Provides the csvarray object to be dumped as a sectioned INI file.
 *
 * \param writefooter
 *    Provides an optional parameter, defaulting to true, to determine if
 *    the footer at the bottom of the file should be written.  This value
 *    will be set to false if writing both drums and patch to the file.
 *
 * \return
 *    Returns 'true' if all operations succeeded.
 */

bool
write_sectioned_drum_file
(
   const std::string & filespec,
   const csvarray & csv,
   bool writefooter
)
{
   std::ofstream output(filespec.c_str());
   bool result = output.good();
   if (result)
   {
      output
         << "# Generated mostly by 'midicvtpp --csv-drum "
         << csv.source_file() << _NL
         << "#   --output " << filespec << "'" << _NL
         << "#" << _NL
         << s_explanatory_header
         << _NL
         ;

      output
         << GM_INI_FILE_STYLE       << " = sectioned" << _NL
         << GM_INI_SETUP_NAME       << " = "       << csv.name() << _NL
         << GM_INI_MAP_TYPE         << " = drum"   << _NL
         << GM_INI_GM_CHANNEL       << " = 10"     << _NL
         << GM_INI_DEV_CHANNEL      << " = 10"     << _NL   /* some use ch 16 */
         << GM_INI_EXTRACT_CHANNEL  << " = "       << GM_INI_NO_VALUE << _NL
         << GM_INI_REJECT_CHANNEL   << " = "       << GM_INI_NO_VALUE << _NL
         << GM_INI_REVERSE          << " = false"  << _NL
         << GM_INI_TESTING          << " = false"  << _NL
         << _NL
         ;

      csvarray::Rows::const_iterator row;
      for (row = csv.rows().begin(); row != csv.rows().end(); row++)
      {
         csvarray::Fields::const_iterator field = row->begin();

         output
   << "[ " << DRUM_SECTION << " " << field[DRUM_INDEX_GM_NOTE] << " ]" << _NL
   << _NL
   << DRUM_LABEL_GM_NAME   << " = " << field[DRUM_INDEX_GM_NAME]  << _NL
   << DRUM_LABEL_GM_NOTE   << " = " << field[DRUM_INDEX_GM_NOTE]  << _NL
   << DRUM_LABEL_DEV_NAME  << " = " << field[DRUM_INDEX_DEV_NAME] << _NL
   << DRUM_LABEL_DEV_NOTE  << " = " << field[DRUM_INDEX_DEV_NOTE] << _NL
            ;

         if (field->size() > DRUM_INDEX_GM_EQUIV)
         {
            output
               << DRUM_LABEL_GM_EQUIV
               << " = " << field[DRUM_INDEX_GM_EQUIV] << _NL
               ;
         }
         output << _NL;
      }
      if (writefooter)
         output << "# vim: ts=3 sw=3 et ft=dosini" << _NL;
   }
   return result;
}

/**
 *    Writes a csvarray object to a patch file in a sectioned INI format.
 *
 * \param filespec
 *    Provides the full path to the CSV file to be processed.
 *
 * \param csv
 *    Provides the csvarray object to be dumped as a sectioned INI file.
 *
 * \param writeheader
 *    Provides an optional parameter, defaulting to true, to determine if
 *    the header at the top of the file should be written.  This value
 *    will be set to false if writing both drums and patch to the file.
 *
 * \return
 *    Returns 'true' if all operations succeeded.
 */

bool
write_sectioned_patch_file
(
   const std::string & filespec,
   const csvarray & csv,
   bool writeheader
)
{
   std::ofstream output(filespec.c_str());
   bool result = output.good();
   if (result)
   {
      if (writeheader)
      {
         output
            << "# Generated mostly by 'midicvtpp --csv-patch "
            << csv.source_file() << _NL
            << "#   --output " << filespec << "'" << _NL
            << "#" << _NL
            << s_explanatory_header
            << _NL
            ;

         output
            << GM_INI_FILE_STYLE       << " = sectioned" << _NL
            << GM_INI_SETUP_NAME       << " = "       << csv.name() << _NL
            << GM_INI_MAP_TYPE         << " = patch"  << _NL
            << GM_INI_GM_CHANNEL       << " = 10"     << _NL
            << GM_INI_DEV_CHANNEL      << " = 10"     << _NL
            << GM_INI_EXTRACT_CHANNEL  << " = "       << GM_INI_NO_VALUE << _NL
            << GM_INI_REJECT_CHANNEL   << " = "       << GM_INI_NO_VALUE << _NL
            << GM_INI_REVERSE          << " = false"  << _NL
            << GM_INI_TESTING          << " = false"  << _NL
            << _NL
            ;
      }

      csvarray::Rows::const_iterator row;
      for (row = csv.rows().begin(); row != csv.rows().end(); row++)
      {
         csvarray::Fields::const_iterator field = row->begin();

         output
   << "[ " << PATCH_SECTION << " " << field[PATCH_INDEX_GM_PATCH] << " ]" << _NL
   << _NL
   << PATCH_LABEL_GM_NAME     << " = " << field[PATCH_INDEX_GM_NAME]   << _NL
   << PATCH_LABEL_GM_PATCH    << " = " << field[PATCH_INDEX_GM_PATCH]  << _NL
   << PATCH_LABEL_DEV_NAME    << " = " << field[PATCH_INDEX_DEV_NAME]  << _NL
   << PATCH_LABEL_DEV_PATCH   << " = " << field[PATCH_INDEX_DEV_PATCH] << _NL
         ;

         if (field->size() > PATCH_INDEX_GM_EQUIV)
         {
            output
               << PATCH_LABEL_GM_EQUIV
               << " = " << field[PATCH_INDEX_GM_EQUIV] << _NL
               ;
         }
         output << _NL;
      }
      output << "# vim: ts=3 sw=3 et ft=dosini" << _NL;
   }
   return result;
}

}                 // namespace midipp

/*
 * iniwriting.cpp
 *
 * vim: ts=3 sw=3 et ft=cpp
 */
